-
Notifications
You must be signed in to change notification settings - Fork 1
impl: verify cli signature #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
A new UI setting was introduced to allow users to run unsigned binaries without any input from the user. Defaults to false which means if a binary is unsigned we will ask the user what to do next.
I moved and modified the logic from CliManager.download to a separate http client based on okhttp and retrofit. The refactor will allow us to easily add new steps in the main download method, and also to easily download new resources. Long term we could also re-use the okhttp client to avoid setting twice the same boilerplate (proxy which is missing from CLIManager, hostname verification and other tls settings) between cli downloader and the rest client
From the same source where the cli binary was downloaded. Some of the previous classes like download result were updated to incorporate details like where the file was saved or whether a file was found on the remote
`allowUnsignedBinaryWithoutPrompt` was caching the initial value read from the store, which required a restart of Toolbox for the real value to reflect.
A pop-up dialog is displayed asking the user if he wants to run an unsigned cli version. The pop-up can be skipped if the user configures the `Allow unsigned binary execution without prompt`
Adds logic to verify the CLI against a detached GPG signature with the help of bouncycastle library
This is the key that validates if the gpg signature was tampered
Initially I thought about embedding it as a const string in the code but the string is too big, best to save it as resource file. The code changes are mostly related to loading the key from the file.
Signature verification some operations that are cpu bound that are light, like decoding and decompressing signature data, some medium CPU intensive operations like the cryptographic verifications and a couple of blocking IO operations: - reading the cli file - reading the signature file - reading the public key file These last should run on the IO thread to not block the main thread from drawing the screen. The cpu bound operation should run on the default thread.
previous implementation was selecting only the first key ring from the public key file which turns out to be wrong. Instead, we should keep all the key rings and search signature key id in all the key rings.
Otherwise, at the next Toolbox restart the signature will no longer be verified, and we run into the risk of running unsigned binaries.
`Files.readAllBytes()` uses direct buffers internally which can be filled up quickly when called repeatedly in coroutines, as these buffers are not released quickly by the GC. The scenario can be reproduced by trying to login a couple of times one after the other with signature verification failing each time. Instead, we can avoid memory issues by streaming the cli and feed only blocks of bytes into the signature calculation.
When there is no signature and the user allowed running of unsigned binaries without prompt
A major feature was added
|
||
} | ||
|
||
suspend fun downloadReleasesSignature(showTextProgress: (String) -> Unit): DownloadResult { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method downloadReleasesSignature()
should take value of buildVersion
as an argument.
analogous to downloadCli(buildVersion, showTextProgress)
We will publish signatures for CLI versions retroactively, going back to at least 3 minor releases.
The underlying Google Cloud Storage Bucket will have a following structure:
releases.coder.com
└── coder-cli
└── x.y.z
├── coder-darwin-amd64.asc
├── coder-darwin-arm64.asc
├── coder-linux-amd64.asc
├── coder-linux-arm64.asc
├── coder-linux-armv7.asc
├── coder-windows-amd64.exe.asc
└── coder-windows-arm64.exe.asc
└── x.{y-1}.z
├── coder-darwin-amd64.asc
├── coder-darwin-arm64.asc
├── coder-linux-amd64.asc
├── coder-linux-arm64.asc
├── coder-linux-armv7.asc
├── coder-windows-amd64.exe.asc
└── coder-windows-arm64.exe.asc
└── x.{y-2}.z
├── coder-darwin-amd64.asc
├── coder-darwin-arm64.asc
├── coder-linux-amd64.asc
├── coder-linux-arm64.asc
├── coder-linux-armv7.asc
├── coder-windows-amd64.exe.asc
└── coder-windows-arm64.exe.asc
patch versions will also be differentiated
Thus, the detached signature should be fetched from the corresponding path ie.
return downloadSignature(URI.create("https://releases.coder.com/coder-cli/$buildVersion").toURL(), showTextProgress)
This PR introduces support for verifying the CLI binary using a detached PGP signature. Starting with version 2.24, Coder signs all CLI binaries. For clients using older versions or running TBX in air-gapped environments, unsigned CLIs can still be executed—either without a prompt or with an optional confirmation prompt.
In terms of code changes - the PR includes a big refactor around CLI downloading with most of the code refactored and extracted in various components that provide clean steps and result state in the main download method. Then the pgp verification logic was added on top, with some particularities: